using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using HCore.Unity.Core;
using HCore.Utility;
using UnityEngine;
using Debug = UnityEngine.Debug;

namespace Test
{
    public class TestBezier : MonoBehaviour
    {
        // Lets create some control points for us to test.
        // Just create some dummy control points.
        // We could adjust and add new control points through the
        // inspector. We will also allow inserting new control
        // points by double clicking on the screen.
        public List<Vector2> ControlPoints = new()
        {
            new Vector2(-5.0f, -5.0f),
            new Vector2(0.0f, 2.0f),
            new Vector2(5.0f, -2.0f)
        };

        // Keep a reference to the point prefab. This prefab 
        // will represent each control point.
        public GameObject PointPrefab;

        // Store the properties of the line.
        public float LineWidth = 0.05f;
        public float LineWidthBezier = 0.05f;
        public Color LineColor = new(0.7f, 0.2f, 0.5f, 0.8f);
        public Color BezierCurveColor = new(0.7f, 0.6f, 0.2f, 0.8f);

        // Keep a list of the points instantiated from the prefab
        // to show the control points.
        private readonly List<GameObject> mPointGameObjects = new();

        // We will also use LineRenderer to show the straight
        // lines connecting these control points as well as
        // the bezier curve based on these control points.
        private LineRenderer[] mLineRenderers;

        // Start is called before the first frame update
        private void Start()
        {
            // Here we will create the actual lines.
            mLineRenderers = new LineRenderer[2];
            mLineRenderers[0] = CreateLine();
            mLineRenderers[1] = CreateLine();

            // Set the name of these lines to distinguish.
            mLineRenderers[0].gameObject.name = "LineRenderer_obj_0";
            mLineRenderers[1].gameObject.name = "LineRenderer_obj_1";

            // Now create the instances of the control points.
            for (var i = 0; i < ControlPoints.Count; i++)
            {
                var obj = Instantiate(PointPrefab, ControlPoints[i], Quaternion.identity);
                obj.name = "ControlPoint_" + i;
                mPointGameObjects.Add(obj);
            }
        }

        // Update is called once per frame
        private void Update()
        {
            // We will now draw the lines every frame.
            var lineRenderer = mLineRenderers[0];
            var curveRenderer = mLineRenderers[1];

            var pts = mPointGameObjects.Select(t => t.transform.position).Select(dummy => (Vector2)dummy).ToList();

            // set the lineRenderer for showing the straight lines between
            // the control points.
            lineRenderer.positionCount = pts.Count;
            for (var i = 0; i < pts.Count; i++) lineRenderer.SetPosition(i, pts[i]);

            // We can now see the straight lines connecting the control points.
            // We will now proceed to draw the curve based on the bezier points.
            var watch = Stopwatch.StartNew();
            var curve = BezierCurves.GenerateBezierCurves(pts);
            watch.Stop();
            Debug.Log($"------->{watch.ElapsedMilliseconds}");
            // var curve = BezierCurves.ComputeVector2List(pts);
            curveRenderer.startColor = BezierCurveColor;
            curveRenderer.endColor = BezierCurveColor;
            curveRenderer.positionCount = curve.Count;
            curveRenderer.startWidth = LineWidthBezier;
            curveRenderer.endWidth = LineWidthBezier;

            for (var i = 0; i < curve.Count; i++) curveRenderer.SetPosition(i, curve[i]);
        }

        // Now let's add functionality to add new control points
        // by double clicking on the screen.
        private void OnGUI()
        {
            var e = Event.current;
            if (!e.isMouse) return;
            if (e.clickCount != 2 || e.button != 0) return;

            var rayPos = new Vector2(
                GameUtils.MainCamera.ScreenToWorldPoint(Input.mousePosition).x,
                GameUtils.MainCamera.ScreenToWorldPoint(Input.mousePosition).y);

            InsertNewControlPoint(rayPos);
        }

        // Let's create a function to create the line renderer.
        private LineRenderer CreateLine()
        {
            var obj = new GameObject();
            var lr = obj.AddComponent<LineRenderer>();
            lr.material = new Material(Shader.Find("Sprites/Default"));
            lr.startColor = LineColor;
            lr.endColor = LineColor;
            lr.startWidth = LineWidth;
            lr.endWidth = LineWidth;
            return lr;
        }

        private void InsertNewControlPoint(Vector2 p)
        {
            var obj = Instantiate(PointPrefab, p, Quaternion.identity);
            obj.name = "ControlPoint_" + mPointGameObjects.Count;
            mPointGameObjects.Add(obj);
        }
    }
}